package com.anji.plus.gaea.security.security.handler;

import com.alibaba.fastjson.JSONObject;
import com.anji.plus.gaea.GaeaProperties;
import com.anji.plus.gaea.bean.ResponseBean;
import com.anji.plus.gaea.cache.CacheHelper;
import com.anji.plus.gaea.constant.GaeaConstant;
import com.anji.plus.gaea.constant.GaeaKeyConstant;
import com.anji.plus.gaea.security.GaeaSecurityProperties;
import com.anji.plus.gaea.security.cache.CacheKeyEnum;
import com.anji.plus.gaea.security.code.UserResponseCode;
import com.anji.plus.gaea.security.event.UserLoginEvent;
import com.anji.plus.gaea.security.i18.GaeaMessageSourceAccessor;
import com.anji.plus.gaea.security.i18.GaeaSecurityMessageSource;
import com.anji.plus.gaea.security.security.extension.UserDetailsServiceHelper;
import com.anji.plus.gaea.utils.ApplicationContextUtils;
import com.anji.plus.gaea.utils.GaeaDateUtils;
import com.anji.plus.gaea.utils.GaeaUtils;
import com.anji.plus.gaea.utils.JwtBean;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.util.CollectionUtils;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.anji.plus.gaea.constant.GaeaKeyConstant.USER_ROLE_SET_PREFIX;

/**
 * 登录成功
 * @author lr
 * @since 2021-01-27
 */
public class GaeaLoginSuccessHandler implements AuthenticationSuccessHandler {

    @Autowired
    private CacheHelper cacheHelper;

    @Autowired
    private JwtBean jwtBean;

    @Autowired
    private GaeaProperties gaeaProperties;

    @Autowired
    private GaeaSecurityProperties gaeaSecurityProperties;

    @Autowired
    private UserDetailsServiceHelper userDetailsServiceHelper;

    private String[] dateArray = new String[]{"day","hour","minute","second"};

    private GaeaMessageSourceAccessor messages = GaeaSecurityMessageSource.getAccessor();

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        response.setCharacterEncoding(GaeaConstant.CHARSET_UTF8);
        response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
        String code = UserResponseCode.USER_LOGIN_SUCCESS;
        ResponseBean.Builder builder = ResponseBean.builder();
        String username = authentication.getName();

        List<String> userOrgCodes = userDetailsServiceHelper.getUserOrgCodes(username);
        //没有任何组织时
        if (CollectionUtils.isEmpty(userOrgCodes)) {
            String noRole = UserResponseCode.USER_HAS_NO_ROLE;
            String noRoleMessage = messages.getMessage(noRole, noRole);
            response.getWriter().print(JSONObject.toJSONString(ResponseBean.builder().code(noRole).message(noRoleMessage).build()));
            return;
        }

        //该用户独有的私钥
        //用于标识不同的客户端，当允许多点登录时，只需要删掉Redis中uuid对应的token即可，其他点登录不影响
        String uuid = GaeaUtils.UUID();
        String token = jwtBean.createToken(username, uuid);

        //保存登录token,并默认两个小时
        cacheHelper.stringSetExpire(GaeaKeyConstant.USER_LOGIN_TOKEN + username + GaeaConstant.REDIS_SPLIT + uuid, token,
                gaeaProperties.getSecurity().getJwtTokenTimeout(), TimeUnit.MINUTES);

        //删除错误次数
        cacheHelper.delete(CacheKeyEnum.USER_PASSWORD_ERROR_NUMBER.getKey() + username);


        //用户对应的机构角色
        Map<String, String> userOrgRoles = userDetailsServiceHelper.getUserRoles(username);

        //设置角色缓存，先删除后添加
        cacheHelper.delete(USER_ROLE_SET_PREFIX + username);
        cacheHelper.hashSet(USER_ROLE_SET_PREFIX + username, userOrgRoles);

        Map<String,String> data = new HashMap(2);
        data.put("token", token);
        data.put("orgCode", userOrgCodes.get(0));
        ResponseBean responseBean = builder.code(UserResponseCode.SUCCESS).data(data).build();

        String message = null;


        //离密码过期还有24小时时，提示用户修改密码
        LocalDateTime passwordUpdateTime = userDetailsServiceHelper.getPasswordUpdateTime(username);
        if (passwordUpdateTime != null) {
            //过期时长
            int expireSeconds = gaeaSecurityProperties.getCredentialsExpiredLength() * 24 * 3600;
            //到期时间点
            LocalDateTime expireLocalDateTime = passwordUpdateTime.plusSeconds(expireSeconds);

            //提醒倒计时
            int credentialsExpiredRemind = gaeaSecurityProperties.getCredentialsExpiredRemind() * 24 * 3600;

            //当前时间距离到期时间点小于提醒时间时，则提示修改密码
            long expireBetween = LocalDateTime.now().until(expireLocalDateTime, ChronoUnit.SECONDS);
            if (expireBetween < credentialsExpiredRemind) {
                code = UserResponseCode.USER_CREDENTIALS_EXPIRED_REMIND;

                //获取时间格式如：11${hour}6${minute}40${second}，根据国际化，替换其中的占位符如：${hour}
                String timeStr = GaeaDateUtils.formatFromSecond(expireBetween);
                Map<String, Object> map = Arrays.stream(dateArray).collect(Collectors.toMap(key -> key, key -> messages.getMessage(key, key), (v1, v2) -> v2));
                message = messages.getMessage(code, new Object[]{GaeaUtils.replaceFormatString(timeStr, map)},code);
            }
        }

        if (StringUtils.isBlank(message)) {
            message = messages.getMessage(code,code);
        }
        responseBean.setMessage(message);
        response.getWriter().print( JSONObject.toJSONString(responseBean));
        //发布登录事件
        ApplicationContextUtils.publishEvent(new UserLoginEvent(username));
    }
}
